package com.ruoyi.web.controller.system;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;

import jakarta.annotation.PostConstruct;

/**
 * 文件上传断点续传
 */
@RestController
@RequestMapping("/system/file")
public class SysFileController extends BaseController {
    
    @Value("${ruoyi.profile}")
    private String uploadPath;

    private String absoluteUploadPath;
    
    // 存储文件上传信息
    private static final Map<String, Map<Integer, String>> FILE_UPLOAD_INFO = new ConcurrentHashMap<>();
    
    @PostConstruct
    public void init() {
        // 确保使用绝对路径
        File uploadDir = new File(uploadPath);
        if (!uploadDir.isAbsolute()) {
            uploadDir = uploadDir.getAbsoluteFile();
        }
        absoluteUploadPath = uploadDir.getPath();
        
        // 创建主上传目录
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }
        
        // 创建分片目录
        File chunksDir = new File(absoluteUploadPath, "chunks");
        if (!chunksDir.exists()) {
            chunksDir.mkdirs();
        }
        
        logger.info("文件上传路径初始化完成：" + absoluteUploadPath);
    }

    /**
     * 上传文件分片
     */
    @PostMapping("/chunk")
    public AjaxResult uploadChunk(MultipartFile file, String fileId, Integer chunkIndex,
            Long start, Long end, Integer total) throws IOException {
        // 创建分片存储目录
        String chunkDir = absoluteUploadPath + File.separator + "chunks" + File.separator + fileId;
        File dir = new File(chunkDir);
        if (!dir.exists()) {
            boolean created = dir.mkdirs();
            if (!created) {
                return AjaxResult.error("无法创建目录：" + chunkDir);
            }
        }
        
        // 保存分片文件
        String chunkPath = chunkDir + File.separator + chunkIndex;
        File chunk = new File(chunkPath);
        
        try {
            // 确保目标目录存在
            chunk.getParentFile().mkdirs();
            // 转储文件
            file.transferTo(chunk);
            
            // 记录分片信息
            Map<Integer, String> chunks = FILE_UPLOAD_INFO.computeIfAbsent(fileId, k -> new HashMap<>());
            chunks.put(chunkIndex, chunkPath);
            
            return AjaxResult.success();
        } catch (IOException e) {
            logger.error("保存分片文件失败：" + e.getMessage(), e);
            return AjaxResult.error("保存分片文件失败：" + e.getMessage());
        }
    }
    
    /**
     * 获取已上传的分片信息
     */
    @GetMapping("/chunks/{fileId}")
    public AjaxResult getUploadedChunks(@PathVariable String fileId) {
        Map<Integer, String> chunks = FILE_UPLOAD_INFO.get(fileId);
        List<Integer> uploadedChunks = new ArrayList<>();
        
        if (chunks != null) {
            uploadedChunks.addAll(chunks.keySet());
        }
        
        Map<String, Object> data = new HashMap<>();
        data.put("chunks", uploadedChunks);
        
        return AjaxResult.success(data);
    }
    
    /**
     * 合并文件分片
     */
    @PostMapping("/merge/{fileId}")
    public AjaxResult mergeChunks(@PathVariable String fileId) throws IOException {
        Map<Integer, String> chunks = FILE_UPLOAD_INFO.get(fileId);
        if (chunks == null || chunks.isEmpty()) {
            return AjaxResult.error("没有找到文件分片");
        }
        
        // 创建最终文件
        String finalPath = absoluteUploadPath + File.separator + fileId;
        File finalFile = new File(finalPath);
        
        try {
            // 确保目标目录存在
            finalFile.getParentFile().mkdirs();
            
            try (RandomAccessFile raf = new RandomAccessFile(finalFile, "rw")) {
                // 按顺序合并分片
                for (int i = 0; i < chunks.size(); i++) {
                    String chunkPath = chunks.get(i);
                    if (chunkPath == null) {
                        return AjaxResult.error("文件分片不完整");
                    }
                    
                    // 写入分片数据
                    File chunk = new File(chunkPath);
                    byte[] buffer = Files.readAllBytes(chunk.toPath());
                    raf.write(buffer);
                    
                    // 删除分片文件
                    Files.delete(chunk.toPath());
                }
            }
            
            // 清理分片目录
            String chunkDir = absoluteUploadPath + File.separator + "chunks" + File.separator + fileId;
            FileUtils.deleteDirectory(new File(chunkDir));
            
            // 清理上传信息
            FILE_UPLOAD_INFO.remove(fileId);
            
            // 返回相对路径
            String url = "/profile/" + fileId;
            Map<String, Object> data = new HashMap<>();
            data.put("url", url);
            data.put("fileName", fileId);
            
            return AjaxResult.success(data);
        } catch (IOException e) {
            logger.error("合并文件失败：" + e.getMessage(), e);
            return AjaxResult.error("合并文件失败：" + e.getMessage());
        }
    }

    /**
     * 获取文件URL
     */
    @GetMapping("/url/{fileId}")
    public AjaxResult getFileUrl(@PathVariable String fileId) {
        // 返回完整的文件路径
        File file = new File(absoluteUploadPath, fileId);
        if (!file.exists()) {
            return AjaxResult.error("文件不存在");
        }
        
        // 返回相对路径，前端会自动加上 baseUrl
        String url = "/profile/" + fileId;
        Map<String, Object> data = new HashMap<>();
        data.put("url", url);
        data.put("fileName", fileId);
        return AjaxResult.success(data);
    }
} 